#pragma once 

/* 实现语音识别子服务部分的封装 */

#include <brpc/server.h>
#include <butil/logging.h>
#include "asr.hpp"      //语音识别模块封装
#include "etcd.hpp"     //服务注册模块封装
#include "logger.hpp"   //日志模块封装
#include "speech.pb.h"  //ProtoBuf框架代码

namespace ChatSystem
{
    // 1. 创建rpc服务子类继承pb(protobuf)中的SpeechService服务类，并实现内部的业务接口逻辑；
    class SpeechServiceImpl : public ChatSystem::SpeechService
    {
    public:
        SpeechServiceImpl(const ASRClient::ptr &asr_client)
        :_asr_client(asr_client)
        {}
        ~SpeechServiceImpl(){}
        //语音识别的处理接口
        void SpeechRecognition(google::protobuf::RpcController* controller,
            const ::ChatSystem::SpeechRecognitionReq* request,
            ChatSystem::SpeechRecognitionRsp* response,
            google::protobuf::Closure* done) override
            {
                //使用ClosureGurd类管理done对象，防止出现未调用Run方法；
                brpc::ClosureGuard rpc_gurd(done);
                //1. 取出请求中的语音数据
                // std::string data = request->speech_content();
                //2. 调用语音SDK模块进行语音识别，得到响应
                std::string err;
                std::string res = _asr_client->recognize(request->speech_content(), err);
                //语音识别是有可能失败的，失败后返回的是一个空字符串，所以需要结果的校验
                if(res.empty())
                {
                    LOG_ERROR("{} 语音识别失败！", request->request_id());
                    response->set_request_id(request->request_id());
                    response->set_success(false);
                    response->set_errmsg("语音识别失败: " + err);
                    return;
                }
                //3. 组织响应
                response->set_request_id(request->request_id());
                response->set_success(true);
                response->set_recognition_result(res);
            }
    private:
        ASRClient::ptr _asr_client; //语音识别客户端
    };



    //语音识别子服务器搭建的流程类
    class SpeechServer
    {
    public:
        using ptr = std::shared_ptr<SpeechServer>;
        SpeechServer(const ASRClient::ptr& asr_client, const Registry::ptr& reg_client, const std::shared_ptr<brpc::Server>& rpc_server)
        :_asr_client(asr_client), _reg_client(reg_client), _rpc_server(rpc_server)
        {}
        ~SpeechServer(){}

        //搭建RPC服务器，并启动服务器
        /* make_asr_object 接口 和 make_reg_object 接口的调用必须在start接口调用之前，
        因为start涉及到了启动服务器，而启动服务器的前提是客户端对象和服务注册客户端对象已经实例化好了; 
        为了防止使用者在start接口调用之前未调用 make_asr_object 接口 和 make_reg_object 接口而导致
        服务器无法启动的问题，我们可以用建造者模式，将构造过程进行控制，在代码中规定调用顺序，去倒逼使用者;
        使用建造者模式的原因还是由于构造SpeechServer对象时参数太多，为了简化SpeechServer对象的构造 */
        void start()
        {
            // 阻塞等待服务端运行结束
            _rpc_server->RunUntilAskedToQuit();
        }
    private:
        ASRClient::ptr _asr_client; //语音识别客户端
        Registry::ptr _reg_client;  //注册中心客户端
        std::shared_ptr<brpc::Server> _rpc_server;   //brpc服务器
    };

    //建造者模式---用来构造SpeechServer对象，将SpeechServer对象的真正构造过程给封装起来，通过封装的类去构造 SpeechServer对象
    class SpeechServerBuilder
    {
    public:
        //构造语音识别客户端对象
        void make_asr_object(const std::string& app_id,
            const std::string& api_key,
            const std::string& secret_key)
        {
            _asr_client = std::make_shared<ASRClient>(app_id, api_key, secret_key);
        }

        //用于构造服务注册客户端对象
        void make_reg_object(const std::string& reg_host,  //注册中心的地址
            const std::string& service_name,    //服务名称
            const std::string& access_host)     //提供服务器的主机的地址信息
        {
            _reg_client = std::make_shared<Registry>(reg_host);
            _reg_client->registry(service_name, access_host);
        }

        //用于构建RPC服务器
        void make_rpc_server(uint16_t port, 
            uint32_t timeout,      //连接空闲超时时间---超时后连接关闭
            uint8_t num_threads)    //rpc服务器的线程数量---IO线程数量；
        {
            _rpc_server = std::make_shared<brpc::Server>();
            // _asr_client 的实例化必须在 speech_service之前进行
             if(!_asr_client)
            {
                LOG_ERROR("还未初始化语音识别模块！");
                abort();
            }
            // 向rpc服务器类中添加rpc子服务对象---告诉服务器收到什么请求用哪个接口处理；
            /*注意：如果在speech_service对象是栈上创建的，AddService的第一个参数就是speech_service对象的地址，当
            make_rpc_server函数调用结束后speech_service这个对象是被销毁了的，而在 _rpc_server中会使用到 speech_service对象
            此时就会出现段错误，所以我们这里需要在堆上创建对象，这样make_rpc_server函数调用结束后再使用speech_service对象也不会错。*/
            SpeechServiceImpl* speech_service = new SpeechServiceImpl(_asr_client);
            /* SERVER_DOESNT_OWN_SERVICE 表示添加服务失败时，服务器也不会删除服务器对象； SERVER_OWNS_SERVICE 表示添加服务失败时，
            服务器将负责删除服务对象，当我们在堆上创建speech_service对象时需要使用SERVER_OWNS_SERVICE，就不会造成内存泄漏。 */
            int ret = _rpc_server->AddService(speech_service,
                brpc::ServiceOwnership::SERVER_OWNS_SERVICE); 
            if(ret == -1)
            {
                LOG_ERROR("添加Rpc服务失败！ ");
                abort();
            }
            brpc::ServerOptions options;
            options.idle_timeout_sec - timeout;
            options.num_threads = num_threads;
            ret = _rpc_server->Start(port, &options);
            if(ret == -1)
            {
                LOG_ERROR("服务器启动失败！");
                abort();
            }
        }

        //构造一个 SpeechServer对象
        SpeechServer::ptr build()
        {
            if(!_asr_client)
            {
                LOG_ERROR("还未初始化语音识别模块！");
                abort();
            }
            if(!_reg_client)
            {
                LOG_ERROR("还未初始化服务注册模块");
                abort();
            }
            if(!_rpc_server)
            {
                LOG_ERROR("还未初始化RPC服务器模块");
                abort();
            }
            SpeechServer::ptr server = std::make_shared<SpeechServer>(_asr_client, _reg_client, _rpc_server);
            return server;
        }
    private:
        ASRClient::ptr _asr_client; //语音识别客户端
        Registry::ptr _reg_client;  //注册中心客户端
        std::shared_ptr<brpc::Server> _rpc_server;   //brpc服务器
    };

}